﻿#include "pch.h"
#include "ChildTaskThread.h"
#include "AsyncLibImpl.h"

namespace AsyncLib
{

CChildTaskThread::CChildTaskThread(ATID atid) : m_atid(atid), m_hThread(NULL)
{
}

CChildTaskThread::~CChildTaskThread()
{
	assert(m_hThread == NULL);
}

void CChildTaskThread::Stop()
{
	m_condVar.notify_one();
}

void CChildTaskThread::Exit()
{
	if (m_hThread == NULL)
		return;

	// Wait for the thread to exit
	WaitForSingleObject(m_hThread, INFINITE);
	CloseHandle(m_hThread);
	m_hThread = NULL;

	// Destruct pending tasks sequentially
	while (m_deqTasks.size())
	{
		m_deqTasks.pop_front();
	}
}

void CChildTaskThread::PostTask(std::function<void()>&& fnTask)
{
	std::unique_lock lock(m_mutex);
	m_deqTasks.push_back(std::move(fnTask));

	// The thread is created on the first call
	if (m_hThread == NULL)
	{
		auto ThreadProc = [](LPVOID pParam)
		{
			((CChildTaskThread *)pParam)->RunTask();
			return 0u;
		};

		m_hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, this, 0, NULL);
	}
	else
		m_condVar.notify_one();
}

void CChildTaskThread::RunTask()
{
	g_asyncLib.SetInTaskThread(m_atid);
	std::unique_lock lock(m_mutex);

	while (g_asyncLib.IsRunning())
	{
		if (m_deqTasks.size())
		{
			auto fnTask = std::move(m_deqTasks.front());
			m_deqTasks.pop_front();
			lock.unlock();

			fnTask();
			fnTask = nullptr;

			lock.lock();
		}
		else
			m_condVar.wait(lock);
	}
}

} // namespace AsyncLib
